//
// Created by YueChuan on 2023/4/13.
//

#ifndef __YUECHUAN_SOCKET_H
#define __YUECHUAN_SOCKET_H

#include "address.h"
#include <memory>
#include "noncopyable.h"
#include <openssl/ssl.h>
#include <openssl/err.h>
#include <netinet/tcp.h>


namespace yuechuan {
    class Socket : public std::enable_shared_from_this<Socket>, Noncopyable {

    public:
        typedef std::shared_ptr<Socket> ptr;
        typedef std::weak_ptr<Socket> weak_ptr;

        /**
         * 套接字类型
         */
        enum Type {
            TCP = SOCK_STREAM,
            UDP = SOCK_DGRAM
        };

        /**
         * @brief 协议族
         */
        enum Family {
            IPV4 = AF_INET,
            IPV6 = AF_INET6,
            UNIX = AF_UNIX
        };

        /**
         * @brief 创建TCP套接字
         * @param address 地址
         * @return
         */
        static Socket::ptr CreateTCP(Address::ptr address);

        /**
         * @brief 创建UDP套接字
         * @param address 地址
         * @return
         */
        static Socket::ptr CreateUDP(Address::ptr address);

        /**
         * @brief 创建IPV4的TCPSocket
         * @return
         */
        static Socket::ptr CreateTCPSocket();

        /**
         * @brief 创建IPV6的TCPSocket
         * @return
         */
        static Socket::ptr CreateTCPSocket6();

        /**
         * @brief 创建IPV4的UDPSocket
         * @return
         */
        static Socket::ptr CreateUDPSocket();

        /**
         * @brief 创建IPV6的UDPSocket
         * @return
         */
        static Socket::ptr CreateUDPSocket6();

        /**
         * @brief 创建Unix的TCPSocket
         * @return
         */
        static Socket::ptr CreateUnixTCPSocket();

        /**
         * @brief 创建Unix的UDPSocket
         * @return
         */
        static Socket::ptr CreateUnixUDPSocket();

        /**
         * @brief 含参构造函数
         * @param family 协议族
         * @param type 类型
         * @param protocol 协议
         */
        Socket(int family, int type, int protocol = 0);

        /**
         * @brief 析构方法
         */
        ~Socket();

        /**
         * @brief 传入一个文件句柄进行初始化
         * @param sock 文件句柄
         * @return bool 初始化是否成功
         */
        virtual bool init(int sock);

        /**
         * @brief 获取发送超时时间
         * @return int64_t
         */
        int64_t getSendTimeout() const;

        /**
         * @brief 设置发送超时时间
         */
        void setSendTimeout(uint64_t timeout);

        /**
         * @brief 获取接收时间
         * @return int64_t
         */
        int64_t getRecvTimeout() const;

        /**
         * @brief 设置接收时间
         * @param timeout 接收时间
         */
        void setRecvTimeout(uint64_t timeout);

        /**
         * @brief 获取sockopt
         * @see getsockopt() 内置函数(经过hook后)
         * @param level 协议层
         *              * SOL_SOCKET： 套接字层选项
         *              * IPPROTO_TCP: TCP协议层选项
         * @param opname 套接字选项的名称
                        * SO_REUSEADDR: 允许地址重用
                        * SO_RCVBUF:    接受缓冲区大小
                        * SO_SNDBUF:    发送缓冲区大小
                        * SO_KEEPALIVE: 表示开启TCP的keepalive功能
                        * SO_ERROR:     表示获取套接字选项错误
         * @param optval 表示保存选项值的缓冲区，需要根据optname类型进行转换
         * @param optlen 表示optval缓冲区的大小
         * @return
         */
        bool getOption(int level, int optname, void *optval, size_t *optlen);

        /**
         * @brief 获取sockopt模板
         * @tparam T
         * @param level
         * @param option
         * @param result
         * @return
         */
        template<class T>
        bool getOption(int level, int option, T &result) {
            size_t size = sizeof(result);
            return getOption(level, option, &result, &size);
        }

        /**
         * @brief 设置sockopt
         * @see setsockopt()内置函数(经过hook后)
         * @param level 协议层
                        * SOL_SOCKET： 套接字层选项
                        * IPPROTO_TCP: TCP协议层选项
         * @param optname 套接字选项的名称
                        * SO_REUSEADDR: 允许地址重用
                        * SO_RCVBUF:    接受缓冲区大小
                        * SO_SNDBUF:    发送缓冲区大小
                        * SO_KEEPALIVE: 表示开启TCP的keepalive功能
                        * SO_ERROR:     表示获取套接字选项错误
         * @param optval 保存选项值的缓冲区，需要根据optname的类型进行转换
         * @param optlen 表示optval缓冲区大小
         * @return
         */
        bool setOption(int level, int optname, const void *optval, socklen_t optlen);

        template<class T>
        bool setOption(int level, int optname, const T &result) {
            return setOption(level, optname, &result, sizeof(result));
        }

        /**
         * @brief 接收connect连接
         * @return Socket::ptr 成功返回新连接的socket，失败返回nullptr
         * @pre 必须要bind，listen成功
         */
        virtual Socket::ptr accept();

        /**
         * @brief 绑定地址
         * @param addr 绑定的地址
         * @return bool 是否绑定成功
         */
        virtual bool bind(const Address::ptr addr);

        /**
         * @brief 连接地址
         * @param addr 目标地址
         * @param timeout_ms 连接超时时间
         * @return bool 是否连接成功
         */
        virtual bool connect(const Address::ptr addr, uint64_t timeout_ms = -1);

        /**
         * @brief 重新连接
         * @param timeout_ms 重连的超时时间
         * @return bool
         */
        virtual bool reconnect(int timeout_ms = SOMAXCONN);

        /**
         * @brief 关闭socket
         * @return bool 是否关闭成功
         */
        virtual bool close();

        /**
         * @brief 监听socket
         * @param backlog 未完成连接队列的最大长度
         * @return bool 返回是否监听成功
         * @pre 需要bind成功
         */
        virtual bool listen(int backlog = SOMAXCONN);

        /**
         * @brief 发送数据
         * @param buffer 待发送数据的内存
         * @param length 待发送数据的长度
         * @param flags 标志字
         * @return int
         *          > 0 成功发送对应大小的数据
         *          = 0 socket 被关闭
         *          < 0 socket 出错
         */
        virtual int send(const void *buffer, size_t length, int flags = 0);

        /**
         * @brief 发送数据
         * @param buffers 待发送数据的内存（iovec数据）
         * @param length 待发送数据的长度（iovec长度）
         * @param flags 标志字
         * @return int
         *          > 0 成功发送对应大小的数据
         *          = 0 socket 被关闭
         *          < 0 socket 出错
         */
        virtual int send(const iovec *buffers, size_t length, int flag = 0);


        /**
         * @brief 发送数据到指定地址
         * @param buffer 待发送数据的内存
         * @param length 待发送数据的长度
         * @param to 发送目标的地址
         * @param flags 标志字
         * @return int
         *          > 0 成功发送对应大小的数据
         *          = 0 socket 被关闭
         *          < 0 socket 出错
         */
        virtual int sendTo(const void *buffer, size_t length, Address::ptr to, int flags);


        /**
         * @brief 发送数据到指定地址
         * @param buffers 待发送数据的内存（iovec数据）
         * @param length 待发送数据的长度（iovec长度）
         * @param to 发送目标的地址
         * @param flags 标志字
         * @return int
         *          > 0 成功发送对应大小的数据
         *          = 0 socket 被关闭
         *          < 0 socket 出错
         */
        virtual int sendTo(const iovec *buffers, size_t length, Address::ptr to, int flags);

        /**
         * @brief 接收数据
         * @param buffer 接收数据的内存
         * @param length 接受数据的内存大小
         * @param flag 标志字
         * @return int
         *          > 0 接收到对应大小的数据
         *          = 0  socket被关闭
         *          < 0  出错
         */
        virtual int recv(void *buffer, size_t length, int flag = 0);

        /**
         * @brief 接收数据
         * @param buffer 接收数据的内存(iovec数据)
         * @param length 接受数据的内存大小(iovec长度)
         * @param flag 标志字
         * @return int
         *          > 0 接收到对应大小的数据
         *          = 0  socket被关闭
         *          < 0  出错
         */
        virtual int recv(iovec *buffers, size_t length, int flag = 0);

        /**
         * @brief 从指定地址接收数据
         * @param buffer 接收数据的内存
         * @param length 接收数据的大小
         * @param from 接受地址
         * @param flag 标志字
         * @return int
         *          > 0 接收到对应大小的数据
         *          = 0 socket关闭
         *          < 0 出错
         */
        virtual int recvFrom(void *buffer, size_t length, Address::ptr from, int flag = 0);

        /**
          * @brief 从指定地址接收数据
          * @param buffer 接收数据的内存
          * @param length 接收数据的大小
          * @param from 接受地址
          * @param flag 标志字
          * @return int
          *          > 0 接收到对应大小的数据
          *          = 0 socket关闭
          *          < 0 出错
          */
        virtual int recvFrom(iovec *buffers, size_t length, Address::ptr from, int flag = 0);

        /**
         * @brief 获取远端地址
         * @return Address::prt
         */
        Address::ptr getRemoteAddress();

        /**
         * @brief 获取本地地址
         * @return Address::ptr
         */
        Address::ptr getLocalAddress();

        /**
         * @brief 获取协议族
         * @return int
         */
        int getFamily() const { return m_family; }

        /**
         * @brief 获取状态
         * @return int
         */
        int getType() const { return m_family; }

        /**
         * @brief 获取协议
         * @return int
         */
        int getProtocol() const { return m_protocol; }

        /**
         * @brief 返回是否连接
         * @return bool
         */
        bool isConnect() const { return m_isConnect; }

        /**
         * @brief 是否有效
         * @brief m_sock ！= -1
         * @return
         */
        bool isValid() const;

        /**
         * @brief 返回Socket错误
         * @return int
         */
        int getError();

        /**
         * @brief 输出信息到流中,比如有用户连接
         * @param os 输出控制流
         * @return
         */
        virtual std::ostream &dump(std::ostream &os) const;

        /**
         * @brief 将Socket对象转换为string类型
         * @return std::string
         */
        virtual std::string toString() const;

        /**
         * @brief 返回socket句柄
         * @return int
         */
        int getSock() const { return m_sock; }

        /**
         * @brief 取消读
         * @return bool
         */
        bool cancelRead();

        /**
         * @brief 取消写
         * @return bool
         */
        bool cancelWrite();

        /**
         * @brief 取消接收
         * @return bool
         */
        bool cancelAccept();

        /**
         * @brief 取消全部事件
         * @return bool
         */
        bool cancelAll();


    protected:
        /// socket句柄
        int m_sock;
        /// 协议族
        int m_family;
        /// 类型
        int m_type;
        /// 协议
        int m_protocol;
        /// 是否连接
        bool m_isConnect;
        /// 本地地址
        Address::ptr m_localAddress;
        /// 远端地址
        Address::ptr m_remoteAddress;

    private:
        void initSock();

        void newSock();
    };

    class SSLSocket : public Socket {
    public:
        typedef std::shared_ptr<SSLSocket> ptr;

        static SSLSocket::ptr CreateTcp(Address::ptr address);

        static SSLSocket::ptr CreateTcpSocket();

        static SSLSocket::ptr CreateTcpSocket6();

        SSLSocket(int family, int type, int protocol = 0);

        bool bind(const Address::ptr addr) override;

        bool connect(const Address::ptr, uint64_t timeout_ms) override;

        bool listen(int backlog = SOMAXCONN) override;

        Socket::ptr accept() override;

        bool close() override;

        int send(const void *buffer, size_t length, int flags = 0) override;

        int send(const iovec *buffers, size_t length, int flags = 0) override;

        int sendTo(const void *buffer, size_t length, const Address::ptr to, int flag = 0) override;

        int sendTo(const iovec *buffers, size_t length, const Address::ptr to, int flags = 0) override;

        int recv(void *buffer, size_t length, int flag = 0) override;

        int recv(iovec *buffers, size_t length, int flags = 0) override;

        int recvFrom(void *buffer, size_t length, Address::ptr addr, int flag = 0) override;

        int recvFrom(iovec *buffers, size_t length, Address::ptr addr, int flags = 0) override;

        bool loadCertificates(const std::string &cert_file, const std::string &key_file);

        std::ostream &dump(std::ostream &os) const override;

    protected:
        bool init(int sock) override;

    private:
        std::shared_ptr<SSL_CTX> m_ctx;
        std::shared_ptr<SSL> m_ssl;
    };

    std::ostream &operator<<(std::ostream &os, const Socket &sock);
}// namespace yuechuan



#endif //YUECHUAN_SOCKET_H
